#!perl
################################################################################
#  Copyright (c) by Medtronic, Inc., 2001.  
#  $Source: /cvs/repository/crm_fw_tools/build/build7.pl,v $
#  $Revision: 61 $
#  $Date: 2013-06-25 20:16:09 +0000 (Tue, 25 Jun 2013) $
#
#  build.pl  -- Main perl script for the build process.  
#
#  Syntax    -- perl build.pl project build_type scr_release
# 
#               project     = (c1g1 | c1g2 | dummy)
#               build_type  = (cron | base)
#               scr_release = (C1G1_FW1 | C1G1_FW2 | C1G1_FW3 | C1G1_FW4 | C1G2_FW1 etc)
#
#  This tools assumes that the file pointed to by $ValidProjectFile has been updated
#  to contain the project name passed in as $ARGV[0] along with the size in bytes of
#  the hex file ($hex_file) and the location of the ROM CRCs in the hex file ($hex_file)
#  delimited by commas (and both in hexadecimal).
################################################################################

$tool_rev = '\$Revision: 61 $';
$tool_rev =~ s/^.*Revision:\s*(\S+)\s*\$$/$1/;


$TRUE=1;
$FALSE=0;

use Cwd;
use File::Copy;
use File::Find;
use DBI;
use DBD::Oracle;

sub Usage;
sub ParseScrDatabase;
sub ParseMakefile;
sub UpdateRev_DateInCode;
sub RunHexFilePostProcessing; 
sub SaveDeliverables;
sub WriteBaseLabelToSCRs;
sub Dumpfile;


#setup database server url
$database_name = 'scr.pacing.medtronic.com';
#configure database source type
$data_source = "dbi:Oracle:$database_name";
$username = 'scr_read';
$password = 'scr_read';

$ENV{'TWO_TASK'} = $database_name;
$ENV{'DBI_DSN'} = $data_source;


$have_error = 1;   #pre-set to force error processing if script dies
$wrote_tags = 0;

$FoundProject        = $FALSE;
$ValidProjectFile    = "c:\\build\\flash_projects.lst";

$PREFIX = "";

if($#ARGV == -1 || $ARGV[0] eq "-h")
{
   Usage();
   exit;
}

if( !( ($ARGV[1] eq "cron") || ($ARGV[1] eq "base") ) )
{
   Usage();
   exit;
}

if( $ARGV[2] eq "" )
{
   Usage();
   exit;
}

if ($ARGV[3] eq "-ignore_warnings")
{
   $ignore_warnings = 1;
}
else
{
   $ignore_warnings = 0;
}

#make user remove any current build directory
if( -e "c:\\build\\.")
{
   die "Remove c:\\build directory!!! $!\n";
}

$cmd = "cls";
system($cmd);

chdir "c:\\" or die "Can't cd to c:\\ $!\n";
$cmd = "md c:\\build";
system($cmd);

chdir "c:\\build" or die "Can't cd to c:\build: $!\n";

$cmd = "cvs -r checkout -p crm_fw_tools/build/flash_projects.lst > flash_projects.lst";
system($cmd);

open(PROJ, "$ValidProjectFile") or die "Can't open $ValidProjectFile\n";  #open and read lists of projects
LINE: while(<PROJ>)
{
   chomp;           #delete newline
	if($_ =~ /^\s*\/\//)
	{
		# skip all lines with // in front - these are comment lines.
		next;
	}
   my @fields = split(/\,/, $_); #parse comma delimited file

    #The first field in $ValidProjectFile contains the project
   if($ARGV[0] eq $fields[0])
   {
      $FoundProject = $TRUE;

      #The next two fields in $ValidProjectFile contain the firmware image size
      #and the trim table size, respectively, delimited by comments.

      $FIRMWARE_SIZE = $fields[1];
      $TRIM_TABLE_SIZE = $fields[2];
      $FIRMWARE_SIZE =~ s/\s//g;   # get rid of spaces if there are any
      $TRIM_TABLE_SIZE =~ s/\s//g;   # get rid of spaces if there are any
      $total_size = hex($FIRMWARE_SIZE) + hex($TRIM_TABLE_SIZE);
      $HEXFILE_SIZE = sprintf("%X", $total_size); 
   }
}
close(PROJ);

if( !$FoundProject)
{
   print "\n\n$ARGV[0] -- Invalid project name! Check $ValidProjectFile!\n\n";
   Usage();
   exit;
}

$project     = $ARGV[0]; #(c1g1 | c1g2 | dummy or any project in project.lst)
$build_type  = $ARGV[1]; #(cron | base)
$scr_release = $ARGV[2]; #map projects to SCR database releases example:C1G1_FW4

if($scr_release eq "")
{
   die "Define an SCR release for project $project!!  $!\n";
}

$dbh = DBI->connect($data_source, $username, $password) or die $DBI::errstr;

#make sure scr release is valid
$command ="select count(SCR.ID) from SCR where SCR.RELEASE='$scr_release'";
$sth = $dbh->prepare($command);
$sth->execute;
($number_of_scrs)=$sth->fetchrow_array();

if($number_of_scrs == 0)
{
   $sth->finish;
   if(defined $dbh) {$dbh->disconnect;}
   die "No scrs for release $scr_release\n";
}

print "The SCR system automatically updates a few minutes after files are checked in.\n";
print "If files have been checked in in the last few minutes, verify that they are\n";
print "attached to the SCR or they will not be included in the build.\n\n";  

$waitTime = 16;  #15 second wait time
$waitStarttime = time; #get Current Time
$waitPrevious = $waitTime;
$waitRemaining = $waitTime;


while($waitRemaining >= 0)
{
   if($waitRemaining < $waitPrevious)
   {
      printf "\rBuild will start in %d seconds.  Press Ctrl-C to exit!",$waitRemaining;
      $waitPrevious = $waitRemaining;  
   }
   $waitRemaining = $waitTime - (time - $waitStarttime); 
} 

$cmd = "cls";
system($cmd);

 
$cmd = "cvs checkout $project/firmware/code/exe/cron.txt";
system($cmd);

# will move cron.txt and base.txt to the build directory so they will be
# unaffected by the build process
move("c:\\build\\$project\\firmware\\code\\exe\\cron.txt", 
                                "c:\\build\\cron.txt") or die "move failed: $!";

if($build_type eq "base")
{
   $cmd = "cvs checkout $project/firmware/code/exe/base.txt";
   system($cmd);
   move("c:\\build\\$project\\firmware\\code\\exe\\base.txt", 
                                   "c:\\build\\base.txt") or die "move failed: $!";
}     

$report_file = $build_type . "_rpt.txt";

open(REPORT_FILE, ">$report_file") ||  die "Can't open $report_file file";
print REPORT_FILE "/*  Copyright (c) by Medtronic, Inc., 2001. \n"; 
printf REPORT_FILE "   \$%s: \$  \$%s: \$  \$%s: \$   */ \n\n","RCSfile","Revision","Date";

print REPORT_FILE "Revision of build7.pl is $tool_rev!\n\n";
 
print REPORT_FILE "===================================================================\n\n";

#*******************************************************************************
#*******************************************************************************       
#
#  parse the friendly scr database and get a sorted list of files; throw away
#  all revisions except the largest one from all of the parsed scr's
#
#*******************************************************************************
#*******************************************************************************
print "Parsing SCR database!\n";

# The output will of ParseScrDatabase will be that @ScrFiles Contains a list of 
# files with the greatest revisions found in the processed SCR's
ParseScrDatabase();

#*******************************************************************************
#*******************************************************************************
#
#  Finished parsing the scr system; @ScrFiles now contains files with their
#  highest revisions
#  Now open up cron.txt or base.txt and and compare the scr file revisions 
#  against the previous build's revisions
#  cron.txt or base.txt may have files that aren't in the scr list because
#  the SCR that had those files may have been baselined
#
#*******************************************************************************
#*******************************************************************************
print "Reading revisions from last build!\n";
open(LAST_BUILD, "$build_type.txt") ||  die "Can't open $build_type.txt file";

LINE: while(<LAST_BUILD>)
{
   chomp;
   s/\s//g;  #get rid of whitespace
 
   next if(/\/\*\s*Copyright/); #ignore copyright line
   
   #find current rev and add 1 to the minor rev
   if( /\s*.+RCSfile:.+Revision:\s*(\d+)\.(\d+)\s*\$.*/)
   {
      $maj_rev = $1;
      $min_rev = $2;
      
      printf REPORT_FILE "Last build version was %d.%d with label %s%d_%d \n",$maj_rev,$min_rev,$build_type,$maj_rev,$min_rev;
      
      #base revisions will be 1.1, 2.1, 3.1
      #cron revisions will be 2.3,2.4   4.6,4.7,  where major revision represents
      #the baseline revision the cron is based on
      if($build_type eq "base")
      {
         $maj_rev++;
         $min_rev = 1;
      }
      else
      {
         if($min_rev >= 255)
         {
            $maj_rev++;
            $min_rev = 1;
         }
         else
         {
            $min_rev++;                    
            
         }
      }
      
      
      $new_revision = sprintf("%d.%d",$maj_rev,$min_rev); 
      $new_label = uc $build_type . $maj_rev . "_" . $min_rev; 
      
      print REPORT_FILE "New build version will be $new_revision with label $new_label!\n\n";
      next;
   }
   push @LastBuildsRevisions, [split/,/];

}
close(LAST_BUILD);

if($new_revision eq ""){ die "Couldn't determine previous build revision: $!";}


$num_files_in_scrs = 0;
$added_files = 0;
$updated_revisions = 0;
@files_added_to_report = ();
@files_updated_in_report = ();
@obsolete_files = ();

for $i (0 .. $#ScrFiles)
{
   #if file is in one of the three obsolete directories, obsolete_code, obsolete_unit_test
   # or obsolete_fds;  assemble obsolete files in an array and remove them
   #from the existing cron.txt or base.txt later
   if(  $ScrFiles[$i][0] =~ /^\/projects\/repository\/.+\/firmware\/obsolete/ )
   {
      push @obsolete_files, $ScrFiles[$i][0];
      next;
   }

   #attic files need to be obsoleted as well
   if(  $ScrFiles[$i][0] =~ /\/Attic\// )
   {
      push @obsolete_files, $ScrFiles[$i][0];
      next;
   }



   #if its not a code,fds or unit_test file, move on please
   
   #don't want files in the exe directory in this list; they are the deliverables
   #that are produced by the build process and will be updated at the end of the script
   next if(  $ScrFiles[$i][0] =~ /^\/projects\/repository\/.+\/firmware\/code\/exe/ );
   
   #skip files that are being used to test the scr system
   next if(  $ScrFiles[$i][0] =~ /^\/projects\/repository\/test/ );
   
   next if(  !( ($ScrFiles[$i][0] =~ /^\/projects\/repository\/.+\/firmware\/code/)    ||
                ($ScrFiles[$i][0] =~ /^\/projects\/repository\/.+\/firmware\/fds_spec/)  ||
                ($ScrFiles[$i][0] =~ /^\/projects\/repository\/.+\/firmware\/unit_test/) ) );
   
   
   $num_files_in_scrs++; 
   
   $found_scr_file = 0;
   
   for $j (0  .. $#LastBuildsRevisions)
   {
      if ($ScrFiles[$i][0] eq $LastBuildsRevisions[$j][0])
      {  
         $found_scr_file = 1;
      
         ## to compare revisions need to format them with leading zeros
         $scr_rev = "";
   
         #converts numbers before the last dot (.)
         while( $ScrFiles[$i][1] =~ /(\d+)\./gc)
         {  
            $scr_rev .= sprintf("%04d.",$1);
         }
   
         #converts the final number after the last dot (.)
         if( $ScrFiles[$i][1] =~ /\G(\S+)/g )
         {
            $scr_rev .= sprintf("%04d",$1);
         }
         
         $last_build_rev = "";
   
         #converts numbers before the last dot (.)
         while( $LastBuildsRevisions[$j][1] =~ /(\d+)\./gc)
         {  
            $last_build_rev .= sprintf("%04d.",$1);
         }
   
         #converts the final number after the last dot (.)
         if( $LastBuildsRevisions[$j][1] =~ /\G(\S+)/g )
         {
            $last_build_rev .= sprintf("%04d",$1);
         }

         
         if($scr_rev gt $last_build_rev)
         {
            #update the LastBuildsRevisions array with the later revision
            $LastBuildsRevisions[$j][1] = $ScrFiles[$i][1];
            $updated_revisions++;
            push @files_updated_in_report, $ScrFiles[$i][0];
         }        
      }
   }
   if($found_scr_file == 0)
   {
      #didn't find scr file in last build; add it to last build array
      push @LastBuildsRevisions, [@{$ScrFiles[$i]}];  
      $added_files++;  
      push @files_added_to_report, $ScrFiles[$i][0];
   }
   

}
if($updated_revisions == 0 && $added_files == 0)
{
   die "ABORT: No files have changed since last build or incorrect SCR release!\n";
}

@SortedRevisions = sort {$a->[0] cmp $b->[0]} @LastBuildsRevisions;

printf REPORT_FILE "Number of files in the current SCR's that were processed processed: %d\n", $num_files_in_scrs;
printf REPORT_FILE "%d files were updated in %s.txt and are as follows:\n\n",$updated_revisions,$build_type;
foreach $filename (@files_updated_in_report)
{
   print REPORT_FILE "    $filename\n";   
}

printf REPORT_FILE "\n%d files were added to %s.txt and are as follows:\n\n",$added_files,$build_type;
foreach $filename (@files_added_to_report)
{
   print REPORT_FILE "    $filename\n";   
}

print REPORT_FILE "\n===================================================================\n\n";

#*******************************************************************************
#*******************************************************************************
#
#  @SortedRevisions now contains a combined list of file that were already
#  in cron.txt or base.txt and new file revisions pulled from the scr's
#
#  Now open the make file and assemble a hash with the files used in it
#  if the file is in @SortedRevisions and not %Files_in_Make; throw them away
#  unless it is a tool file
#  if there are files in %Files_in_Mak that are not in @SortedRevisions, we
#  have a problem, so error out
#
#*******************************************************************************
#*******************************************************************************


#cycle through files in the obsolete list and remove them from the cron.txt or
#base.txt list
for $ob_index (0 .. $#obsolete_files)
{
   $obsolete_files[$ob_index] =~ s/.*\/(.*)$/$1/;

   for $exist_index (0 .. $#SortedRevisions)
   {
      #obsolete files have their path stripped off because they contain the
      #obsolete directory, make a temporary variable with the path stripped
      #off so the compare works out.
      $path_stripped_name = lc $SortedRevisions[$exist_index][0];
      $path_stripped_name =~ s/.*\/(.*)$/$1/;
            
      if(lc $path_stripped_name eq lc $obsolete_files[$ob_index])
      {
          splice(@SortedRevisions,$exist_index,1);
          last;
      }
   }
}

ParseMakefile();

#*******************************************************************************
#*******************************************************************************
#
#  @SortedRevisions now contains an updated list of all of the code files
#  that should be used in the build
#
#  now write a new tag to all of the files  and create a new cron.txt or base.txt
#
#*******************************************************************************
#*******************************************************************************
open (REVISION_FILE, ">$build_type.txt") || die "Can't open $build_type.txt file";
print REVISION_FILE "/*  Copyright (c) by Medtronic, Inc., 2001.  Files for build: $new_label\n"; 
printf REVISION_FILE "   \$%s: \$  \$%s: \$  \$%s: \$   */ \n","RCSfile","Revision","Date";

print "Writing new tag -- $new_label and updating $build_type.txt!\n";

$wrote_tags = 1;  #if we have an error after writing the tags we want to
                  #back out the tags we are about to write


                  
for $x (0 .. $#SortedRevisions)
{
   #update cron.txt or base.txt with the latest revisions
   print REVISION_FILE "$SortedRevisions[$x][0],$SortedRevisions[$x][1]\n";
   
   #only tag code files, but not executables
   next if ( $SortedRevisions[$x][0] =~ /\/code\/exe/ );

   next if(  !($SortedRevisions[$x][0] =~ /^\/projects\/repository\/.+\/firmware\/code/));


   
   $SortedRevisions[$x][0] =~ s/\/projects\/repository\///;
   
   #mark each file with the new label
   $cmd = "cvs rtag -r $SortedRevisions[$x][1] $new_label $SortedRevisions[$x][0]";
 
   printf "\rTagging File %-62s",$SortedRevisions[$x][0];  
 
   $response = `$cmd`;
   
   
   #the cvs response is nothing when there are no errors; if there is, bail out
   #on the build
   if($response ne "")
   {
      print REPORT_FILE "ERROR: $response\n";
      die;
   }
 
}


#*******************************************************************************
#*******************************************************************************
#
#  time to check out the code based on the label that was just written
#
#*******************************************************************************
#*******************************************************************************
print "\nChecking out source code!\n";
print REPORT_FILE "\nChecking out source code!\n\n";
$cmd = "cvs -r checkout -r $new_label $project/firmware/code";

$response = `$cmd`;
print REPORT_FILE "$response\n";
print REPORT_FILE "===================================================================\n\n";


#first create other directories used in the build process
$cmd = "md c:\\build\\$project\\firmware\\code\\lst";
system($cmd);

$cmd = "md c:\\build\\$project\\firmware\\code\\obj";
system($cmd);


print REPORT_FILE "\nChecking out executable files to determine head revisions!\n\n";


#the previous checkout checked out a label which isn't necessarily the head
#revision, want all files in the exe directory to be the head so we'll extract
#those next, we'll open the entries file to see what the head revisions are
#and then use those after we check the files in and want to tag those revisions

#there shouldn't be any exe files in cron.txt or base.txt but if there are
#they will be checked out before with the new label; that will create a sticky
#tag on the directory; the following command will remove it.
$cmd = "cvs update -A $project/firmware/code/exe";
system($cmd);

$cmd = "cvs checkout $project/firmware/code/exe";
$response = `$cmd`;
print REPORT_FILE "$response\n";

#open the cvs directory file that contains the revisions in the exe directory
%Deliverables = ();
open(ENTRIES_FILE, "<c:\\build\\$project\\firmware\\code\\exe\\cvs\\entries") ||  die "Can't open exe\\entries file";
while(<ENTRIES_FILE>)
{
   if(/\/(.+?)\/(.+?)\/.*/)
   {
      $Deliverables{lc $1} = $2;
   }
   
   
}
close(ENTRIES_FILE); 

#delete files in exe directory so they don't accidently get checked in
@DeleteFiles = ();
$dirname = "c:\\build\\$project\\firmware\\code\\exe";
opendir(DIR, $dirname) or die "Can't open directory $dirname: $!"; 

while( defined ($file = readdir DIR))
{
   next if $file =~ /^\.\.?$/; #skip . and .. 
   next if $file =~ /CVS$/i; #skip cvs directory
   push @DeleteFiles, "$dirname\\$file";
}
closedir(DIR);

unlink @DeleteFiles; 

print REPORT_FILE "Removing all files in exe directory!\n\n";
print REPORT_FILE "===================================================================\n\n";


#*******************************************************************************
#*******************************************************************************
#
#  Build some code
#
#*******************************************************************************
#*******************************************************************************
# the following will update the build revision and date in fillcode.asm
UpdateRev_DateInCode();

#building has to occur in the tools dir
chdir "c:\\build\\$project\\firmware\\code\\tools" or 
              die "Can't cd to c:\\build\\$project\\firmware\\code\\tools: $!\n";

$warn_file = "buildoutput.txt";
$warnings = "";

print "Running maker!\n";
$cmd = "..\\archsoft\\maker ..\\make\\bld_loc.mak > buildoutput.txt";
system($cmd);

$hex_file = "c:\\build\\$project\\firmware\\code\\exe\\intel.hex";
$error_file = "c:\\build\\$project\\firmware\\code\\lst\\errors.txt";

#error occurs if the hex_file file does not exist or has a size of zero bytes. 
if( !(-e $hex_file) || (-z $hex_file) )
{
   #problem -- hex file doesn't exist
   print REPORT_FILE "ERROR: Intel.hex file not generated and was not added to CVS!\n";
   
   #copy the contents of the error file to the report file
   Dumpfile(*REPORT_FILE, $error_file);
   
   die;
}
else
{
   
   open(WARN_FILE, "buildoutput.txt") ||  die "Can't open buildoutput.txt!";
   while (<WARN_FILE>)
   {
      if (/\:\s+WARNING/) # If there is a : then white space then the word WARNING in caps, we have warnings in the build.
      {
         $warnings .= $_;       
      }
   }
   close(WARN_FILE);
   
   if ($warnings)
   {
      #print the warning line to the report and save it to print to the screen later
      print REPORT_FILE "\n\nThe Build contained the following warnings:\n";
      print REPORT_FILE $warnings;
      print REPORT_FILE "\n\n";

      # only fail due to warnings if this is a baseline build and the user did not choose to ignore warnings.
      if ($build_type eq "base" && !$ignore_warnings)
      {
         print "\n\nThe Build contained the following warnings:\n";
         print $warnings;

         print "\n\nThe baseline has failed due to warnings in the build.\n";
         print "To override these warnings run the build with:\n";
         print "    build $project base $scr_release -ignore_warnings\n\n";
         print "Note that even if the -ignore_warnings switch is used,\n";
         print "warnings will still be reported in the base_rpt.txt file.\n\n";
      
         die;
      }
   }

   print REPORT_FILE "Files were compiled and linked. Intel.hex created!\n\n";
}

# the following will take the hex file from the linker, add fill code and
# crs's to the hex file;  Also some map files and tsp files are generated
RunHexFilePostProcessing();

print REPORT_FILE "===================================================================\n\n";
print REPORT_FILE "The following SCR's were processed in addition to the SCR's in the last baseline build!\n\n";
foreach $ScrId (keys %AllScrIdList)
{
   print REPORT_FILE "$AllScrIdList{$ScrId}\n";
}
print REPORT_FILE "===================================================================\n\n";

open(SCR_SUMMARY, ">c:\\build\\$project\\firmware\\code\\exe\\codeScrs.txt") || 
       die "Can't open c:\\build\\$project\\firmware\\code\\exe\\codeScrs.txt";

print SCR_SUMMARY "//  Copyright (c) by Medtronic, Inc., 2001. \n"; 
printf SCR_SUMMARY "//  \$ %s \$     \n", "RCSfile: ";
printf SCR_SUMMARY "//  \$ %s \$   \n", "Revision: ";
printf SCR_SUMMARY "//  \$ %s \$     \n\n\n", "Date: ";

printf SCR_SUMMARY "%-8s %-9s %-12s %-60s \n","SCR", "Resolver","Status","Short Description";

foreach $idx (sort keys %CodeScrIdList)
{  
   print SCR_SUMMARY $CodeScrIdList{$idx};   
}

close(SCR_SUMMARY);

if($build_type eq "base")
{
   WriteBaseLabelToSCRs();
}


SaveDeliverables();

$have_error = 0;




# perform any clean up operation
# the end function is called regardless of how we exit; whether normally
# or if it dies
END
{  
   if($have_error == 1)
   {   
      if($wrote_tags == 1)
      {
         #build failed and we already wrote the tags, remove them
         print "Removing Tag Label - $new_label\n";
         $cmd = "cvs rtag -d $new_label $project/firmware"; 
         $response = `$cmd`;
         print "$response\n";
         
      }
      if(-e $report_file)
      {
         print "Check $report_file for error information!\n";
      }
   } 
   else
   {
      print "\nBuild $new_label successful!!!!\n";
     
      $time = time - $^T; 
      printf "Run time = %d minutes, %d seconds\n",$time/60,$time%60;
      
      if ($warnings)
      {
        print "\n\nThe following warnings exist in the build:\n";
        print $warnings;
        
      }
   }
   
   close(REVISION_FILE);
   close(REPORT_FILE);
}


###############################################################################
## Usage()
##
## Description: Prints how the tool should be run.
#  Syntax    -- perl build.pl project build_type scr_release
# 
#               project     = (c1g1 | c1g2 | dummy)
#               build_type  = (cron | base)
#               scr_release	= (C1G1_FW1 | C1G1_FW2 | C1G1_FW3 | C1G1_FW4 | C1G2_FW1)
#
## Input:      None
##
##
## 
###############################################################################
sub Usage
{  
   
   print "  Usage:  build project [cron | base] scr_release\n\n";
   print "          where project = c1g1, c1g2, etc.\n\n";
   print "          and   scr_release = C1G1_FW4, C1G2_FW1, etc.\n\n";
   print "  EX: build c1g1 cron C1G1_FW1\n\n";
   print "  To add a project, update $ValidProjectFile\n";
}

###############################################################################
## Dumpfile()
##
## Description:Opens default.env file in current directory to find location of
##             mapfile.
##
## Input:      $file_handle - file or stdout to write file contents
##             $file_name   - file that will be dumped
##
##
## 
###############################################################################
sub Dumpfile
{
   my($file_handle, $file_name) = @_;

   open (MY_FILE, "<$file_name") || print REPORT_FILE "Can't open $file_name file";
   while(<MY_FILE>)
   {
      print $file_handle $_;
   }
   close(MY_FILE);

}


###############################################################################
## ParseScrDatabase()
##
## Description: Does a query on the scr database and
##
## Input:      $file_handle - file or stdout to write file contents
##             $file_name   - file that will be dumped
##
## Output:     @ScrFiles  -  Contains a list of files with the greatest
##                           revisions found in the processed SCR's
## 
###############################################################################
sub ParseScrDatabase
{
   
   if($build_type eq "base")
   {
      $command = 'SELECT DISTINCT ' .
                 'SCR.ID, ' .
                 'SCR.RESOLVER, ' .
                 'SCR.STATUS, ' .
                 'SCR.SHORT_DESCRIPTION, ' .
                 'LOGS.SERVERPATH, ' .
                 'LOGS.REVISION, ' .
                 'TextStrings.ID, ' .
                 'TextStrings.String ' .
                 'FROM SCR , LOGS, TextStrings ' .
                 "WHERE ( SCR.RELEASE='$scr_release' ) AND " .
                 "( UPPER(SCR.CUSTOM3) LIKE '%YES%' ) AND " .        #code is committed
                 "SCR.STATUS = 'Closed' AND " .                      #by def for base, must be closed
                 'SCR.ID = LOGS.ID AND ' .
                 'SCR.ID = TextStrings.ID AND ' .
                 'TextStrings.String IS NULL AND ' .                 #hasn't been in baseline build yet
                 "SERVERPATH LIKE '%' " .
                 'ORDER BY SERVERPATH ASC, SCR.ID ASC, REVISION ASC';

   
   }
   else
   {
                                  
      $command = 'SELECT DISTINCT ' .
                 'SCR.ID, ' .
                 'SCR.RESOLVER, ' .
                 'SCR.STATUS, ' .
                 'SCR.SHORT_DESCRIPTION, ' .
                 'LOGS.SERVERPATH, ' .
                 'LOGS.REVISION, ' .
                 'TextStrings.ID, ' .
                 'TextStrings.String ' .
                 'FROM SCR , LOGS, TextStrings ' .
                 "WHERE ( SCR.RELEASE='$scr_release' ) AND " .
                 "( UPPER(SCR.CUSTOM3) LIKE '%YES%' ) AND " .        #code is committed
                 'SCR.ID = LOGS.ID AND ' .
                 'SCR.ID = TextStrings.ID AND ' .
                 'TextStrings.String IS NULL AND ' .                 #hasn't been in baseline build yet
                 "SERVERPATH LIKE '%' " .
                 'ORDER BY SERVERPATH ASC, SCR.ID ASC, REVISION ASC';                 
                 
   }

   $sth = $dbh->prepare($command);
   $sth->execute;

   $prev_filepath = "";
   $highest_rev   = "";
   $id_of_hi_rev  = "";
   %AllScrIdList  = ();
   %CodeScrIdList = ();

   #LOGS.SERVERPATH gives the true path to the file in the repository.  It includes the 
   #file's full name.  For this reason, the ,v will need to be removed from CVS files.
   while (($id, $resolver, $scr_status, $scr_description, $filepath, $revision ) = $sth->fetchrow_array)
   {
      $formatted_revision = "";

      $filepath =~ s/\,v$//i;  #remove the CVS ending ,v from $filepath

      #LOGS.SERVERPATH will also return the true path to the file.
      #in the past the symbolic path had been used.  Because all of our
      #files are on the same server (data/pace), data/pace be part of the path.
      #The symbolic link uses projects instead of data/pace.  Also, in order to 
      #allow the move from data/pace/repository to /cvs/repository, /cvs/repository
      #also has to be allowed.  
      #Since all the projects cron.txt and base.txt files use the symbolic links, the 
      #"data/pace" of the true path needs to be exchanged for the "projects"
      #of the symbolic path in order for the pattern matching to work.
      #The other choice would be to modify the cron.txt and base.txt for
      #all existing projects, however updating the build tool was deemed the
      #better solution.
      $filepath =~ s/data\/pace/projects/i; #change data/pace to projects
      $filepath =~ s/cvs\/repository/projects\/repository/i; # change cvs/repository to projects/repository

      # %AllScrIdList contains all of the scrs that were looked at, even if they only include fds or unit tests
      $AllScrIdList{$id} = sprintf "%-8s %-9s %-12s %-60s\n" ,$id, $resolver,$scr_status,$scr_description ; 
  
      if(  $filepath =~ /^\/projects\/repository\/$project\/firmware\/code/)
      {
         # %CodeScrIdList contains only scrs that have code attached to them
         $CodeScrIdList{$id} = sprintf "%-8s %-9s %-12s %-60s\n" ,$id, $resolver,$scr_status,$scr_description ;         
      }
  
      # convert x.y.zz.a to 000x.000y.00zz.000a so compares will work correctly
      while( $revision =~ /(\d+)\./gc)
      {  
         $formatted_revision .= sprintf("%04d.",$1);
      }
   
      if( $revision =~ /\G(\S+)/g )
      {
         $formatted_revision .= sprintf("%04d",$1);
      }
   
      if( ($prev_filepath ne $filepath) && ($prev_filepath ne "") )
      {
         $highest_rev =~ s/0+(\d+)\./$1\./g; #remove leading 0's in front of digit(s) and a dot .
         $highest_rev =~ s/\.0+(\d+)/\.$1/g; #remove leading 0's after final dot .
             
         push @ScrFiles, [$prev_filepath, $highest_rev];
     
         $id_of_hi_rev  = $id;
         $highest_rev   = $formatted_revision;
      }
      elsif ($formatted_revision gt $highest_rev)
      {
         $id_of_hi_rev  = $id;
         $highest_rev   = $formatted_revision;
      }
   
      $prev_filepath = $filepath;
   }

   #print last file
   $highest_rev =~ s/0+(\d+)\./$1\./g;   #remove leading 0's in front of digit(s) and a dot .
   $highest_rev =~ s/\.0+(\d+)/\.$1/g;   #remove leading 0's after final dot .


   push @ScrFiles, [$prev_filepath, $highest_rev];

   $sth->finish;
   if (defined $dbh) {$dbh->disconnect;}
   
}

###############################################################################
## ParseMakefile()
##
## Description: SortedRevisions contains a list of files and its latest committed
##              revision that is combined from the current SCR's and previous 
##              cron.txt or base.txt file.
##
##              First thing to do is search for the revision of the make file
##              that is to be used and check it out. Then all of the *.c, *.h,
##              *.asm, *.prm, and *.mak files are assembled into a hash,
##              %Files_in_Make.
##       
##              Next cycle through $SortedRevisions. If the file is not in the
##              make file and its in the inc, make or src directory, remove
##              it from $SortedRevisions. If there are any files in the make
##              file that aren't in $SortedRevisions, we have a fatal error.
##
## Input:      @SortedRevisions
##
##
## 
###############################################################################
sub ParseMakefile
{

   # first thing to do is to determine which make file revision to use and then
   # go get it
   print "Checking out most recent makefile that is in a committed SCR!\n";
   
   for $x (0 .. $#SortedRevisions)
   {
      if($SortedRevisions[$x][0] =~ /make\/bld_loc.mak/ )
      {
         $cmd = "cvs -r checkout -r $SortedRevisions[$x][1] $project/firmware/code/make/bld_loc.mak";
      
         system($cmd);
         last;
      }
   }
   
   print REPORT_FILE "Comparing files in SCR's to files in makefile!\n";
   print "Processing Make File!!\n";
   
   $make_file ="c:\\build\\$project\\firmware\\code\\make\\bld_loc.mak";

   %Files_in_Make;

   open (MAKE_FILE, "<$make_file") or die "Can't open $make_file file!";
   while(<MAKE_FILE>)
   {
      chomp;
      
      while(/(\w+\.h)(\s+|$)/g)
      {
         $Files_in_Make{lc $1} = 1;
      }

      while(/(\w+\.c)(\s+|$)/g)
      {
         $Files_in_Make{lc $1} = 1;
      }
   
      while(/(\w+\.asm)(\s+|$)/g)
      {
         $Files_in_Make{lc $1} = 1;
      }

      while(/(\w+\.mak)(\s+|$)/g)
      {
         $Files_in_Make{lc $1} = 1;
      }
   
      while(/(\w+\.prm)(\s+|$)/g)
      {
         $Files_in_Make{lc $1} = 1;
      }

   }
   close(MAKE_FILE);


   $x = 0;
   while ($x <= $#SortedRevisions)
   {
      $code_file = "";
   
      #there will be tools and extra files that won't be in the make file
      #however, they won't be in the inc,src or make directories
      if($SortedRevisions[$x][0] =~ /.*code\/inc\/(\w+\.\w+)\s*$/)
      {
         $code_file = $1;
      }   
      elsif($SortedRevisions[$x][0] =~ /.*code\/src\/(\w+\.\w+)\s*$/)
      {
         $code_file = $1;
      }
      elsif($SortedRevisions[$x][0] =~ /.*code\/make\/(\w+\.\w+)\s*$/)
      {
         #hex.bbl is not in the make file
         if($1 ne "hex.bbl")
         {
            $code_file = $1;
         }
      }
   
      if($code_file ne "")
      {
         if(exists($Files_in_Make{lc $code_file}))
         {
            delete $Files_in_Make{lc $code_file};
         }
         else
         {
            print REPORT_FILE "WARNING: Not Found in makefile -- $code_file -- Will not be extracted!\n";
            splice(@SortedRevisions,$x,1);
            next;   
         }
      }
      $x++;
   }

   @filesInMakeNotInBuild = keys %Files_in_Make;


   if($#filesInMakeNotInBuild != -1)
   {
      print REPORT_FILE "\nERROR: The following files are in the make file but are\n";
      print REPORT_FILE "not in an SCR or $build_type.txt:\n";
   
      foreach $y (@filesInMakeNotInBuild)
      {
         print REPORT_FILE "$y\n";
      }
      die;
       
   }
   print REPORT_FILE "===================================================================\n\n";
   
}

###############################################################################
## UpdateRev_DateInCode()
##
## Description: Fillcode.h contains a date substring and the revision of the
##              build.  This routine updates that information in the file.
##
## Input:      None
##
##
## 
###############################################################################
sub UpdateRev_DateInCode
{
   print "Updating fillcode.h with current build and date!\n\n";

      # need to update fillcode.h with the build revision and date
   ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
   $actday=(Sun,Mon,Tue,Wed,Thu,Fri,Sat)[$wday];
   $actmon=(Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$mon];

   $year += 1900;

   #make fillcode.h writable
   $cmd = "cvs edit $project/firmware/code/inc/fillcode.h";
   system($cmd);


   #open up fillcode.h and modify in place without a temporary file
   $fill_file = "c:\\build\\$project\\firmware\\code\\inc\\fillcode.h";
   open(FILL_FILE, "+<$fill_file") or die "Can't open $fill_file file!!\n";

   @fillcode = <FILL_FILE>;

   my $next_line = "";

   foreach $line (@fillcode)
   {
      if($line =~ /_fillcodeBuild_Info/)
      {
         $next_line = "copyright";
         next;
      }
      elsif ($next_line eq "copyright")
      {
         $next_line = "date_time_stamp";
         next;
      }
      elsif ($next_line eq "date_time_stamp")
      {
         $line = sprintf("         \"%3s %3s %2d %02d:%02d:%02d UTC %4d\",\n",
                                 $actday, $actmon, $mday, $hour, $min, $sec, $year);
         $next_line = "base_major_rev";
         next;
      }
      elsif ($next_line eq "base_major_rev")
      {
         if($build_type eq "base")
         {
            $line = sprintf("         %d,\n",$maj_rev);
         }
         else
         {
            $line = sprintf("         0,\n");
         }
         $next_line = "base_minor_rev";
         next;
      }
      elsif($next_line eq "base_minor_rev")
      {
         if($build_type eq "base")
         {
            $line = sprintf("         %d,\n",$min_rev);
         }
         else
         {
            $line = sprintf("         0,\n");
         }
         $next_line = "cron_major_rev";
         next;
      }
      elsif ($next_line eq "cron_major_rev")
      {
         if($build_type eq "cron")
         {
            $line = sprintf("         %d,\n",$maj_rev);
         }
         else
         {
            $line = sprintf("         0,\n");
         }
         $next_line = "cron_minor_rev";
         next;
      }
      elsif($next_line eq "cron_minor_rev")
      {
         if($build_type eq "cron")
         {
            $line = sprintf("         %d,\n",$min_rev);
         }
         else
         {
            $line = sprintf("         0,\n");
         }
         $next_line = "";
         next;
      }
   }

   seek(FILL_FILE, 0, 0);
   print FILL_FILE @fillcode;
   truncate(FILL_FILE, tell(FILL_FILE) );
   close(FILL_FILE);

}

###############################################################################
## RunHexFilePostProcessing()
##
## Description: After hex file is created, add fillcode, crc's and generate
##              TSP maps
##
## Input:      None
##
##
## 
###############################################################################
sub RunHexFilePostProcessing
{

   # run the post processing tools on the generated hex file
   move("c:\\build\\$project\\firmware\\code\\exe\\intel.hex", 
   "c:\\build\\$project\\firmware\\code\\tools\\raw.hex") or die "move failed: $!";

   print REPORT_FILE "Calling hexfill.exe, hexcrcflash.exe and hexverflash.exe to post process intel.hex!\n\n";
   
   print "Running Hexfill.exe!\n";

   $cmd = ".\\hexfill -s $HEXFILE_SIZE -p FF >nul";
   system($cmd);

   #copy the contents of filler.log to the report file
   Dumpfile(*REPORT_FILE, "filler.log");

   print REPORT_FILE "Hexcrcflash output follows!\n\n";
   print "Running Hexcrcflash.exe!\n";

   $cmd = ".\\hexcrcflash -s $FIRMWARE_SIZE -t $TRIM_TABLE_SIZE > nul";
   system($cmd);

   #copy the contents of crcer.log to the report file
   Dumpfile(*REPORT_FILE, "crcer.log");

   print REPORT_FILE "Hexverflash output follows!\n\n";
   print "Running Hexverflash.exe!\n";

   #check for the word 'fail' being written to the screen
	# must do this case insensitive!
   $cmd = ".\\hexverflash -s $FIRMWARE_SIZE -t $TRIM_TABLE_SIZE";

   open VERIFY,"$cmd |" or die "Can't call hexverflash command!: $!";
   while(<VERIFY>)
   {
      chomp;
      if(/fail/i) 
      { 
         $have_error = 1; 
         #copy the contents of verer.log to the report file
         Dumpfile(*REPORT_FILE, "verer.log");
         close(VERIFY);     
         die;
      }
   }                                 
   close(VERIFY);                    
	# If we got here, no error.  Just put results of hexverflash into
	# report file.
	Dumpfile(*REPORT_FILE, "verer.log");

   print REPORT_FILE "\n\nCreating TSP files\n\n"; 
   
   $cmd = ".\\makeTestMapfiles $build_type $project $new_revision ..\\exe\\ext_link.map" ;
	if($PREFIX ne "")
	{
		$cmd .= " -p $PREFIX"
	}
   system($cmd);

   if (-e "createEEprom_sis.exe" and -e "eeprom_structs.lst")  #some projects may not want this
   {
      $cmd = ".\\createEEprom_sis -v ..\\exe\\var_sis.lst -s eeprom_structs.lst" ;
      system($cmd);
      unless (-e "c:\\build\\$project\\firmware\\code\\tools\\eeprom_sis.lst")
      {
         $have_error = 1;
         print REPORT_FILE "\n\nERROR: eeprom_sis.lst was not created!\n\n"; 
         die;
      }
      move("c:\\build\\$project\\firmware\\code\\tools\\eeprom_sis.lst", 
      "c:\\build\\$project\\firmware\\code\\exe\\eeprom_sis.lst");
      
   }

   #cleanup temporary files from the post-processing
   @DeleteFiles = ();
   push @DeleteFiles, "filled.hex";
   push @DeleteFiles, "temp.hex";
   push @DeleteFiles, "symtab.txt";
   push @DeleteFiles, "filler.log";
   push @DeleteFiles, "crcer.log";
   push @DeleteFiles, "verer.log";

   unlink @DeleteFiles;

}

###############################################################################
## SaveDeliverables()
##
## Description: Build was successful; check in all relevant files that were
##              produced by the build.
##
## Input:      None
##
##
## 
###############################################################################
sub SaveDeliverables
{
   move("c:\\build\\$project\\firmware\\code\\tools\\intel.hex", 
   "c:\\build\\$project\\firmware\\code\\exe\\intel.hex") ;
   
   move("c:\\build\\$project\\firmware\\code\\tools\\raw.hex", 
   "c:\\build\\$project\\firmware\\code\\exe\\raw.hex") ;
     
   print REPORT_FILE "\nBuild was successful!!\n";
   
   close(REVISION_FILE);
   close(REPORT_FILE);
   
   $src_file  = sprintf("c:\\build\\%s_rpt.txt",$build_type);
   $dest_file = sprintf("c:\\build\\%s\\firmware\\code\\exe\\%s_rpt.txt",$project,$build_type);
   move("$src_file", "$dest_file");
  
   @ExecFiles = ("intel.hex","raw.hex","var_sis.lst","linkout.abs","ext_link.map","linkout.map","cron.txt","codeScrs.txt");
   if (-e "c:\\build\\$project\\firmware\\code\\exe\\eeprom_sis.lst") #some projects may not use this file
   {
      push @ExecFiles, "eeprom_sis.lst";
   }
      

   push @ExecFiles, sprintf("%s_rpt.txt",$build_type);
   push @ExecFiles, sprintf("%s_m.cpp",$project);
   push @ExecFiles, sprintf("%s_m.h",$project);
     
   if($build_type eq "base")
   {
      push @ExecFiles, "base.txt";
      copy("c:\\build\\base.txt","c:\\build\\cron.txt");
      
      move("c:\\build\\base.txt", 
      "c:\\build\\$project\\firmware\\code\\exe\\base.txt");
   }
   
   move("c:\\build\\cron.txt", 
   "c:\\build\\$project\\firmware\\code\\exe\\cron.txt");
   
   chdir "c:\\build\\$project\\firmware\\code\\exe" or die "c:\\build\\$project\\firmware\\code\\exe: $!\n";
      
      
   print "Checking in Executables! \n";
 
   foreach $finalfile (@ExecFiles)
   {      
      $response = " ";  #preload to cause warning
      
      $exec_rev = $Deliverables{lc $finalfile};
      if($exec_rev =~ /(\d+)\.(\d+)/)
      {
         #explicitly try to tag the next revision then we have currently
         #so if new version wasn't checked in, we wouldn't tag the previous revision
         #by mistake
         $exec_rev = $1 ."." . ($2+1);  
         
              
         if(  ($finalfile eq "cron\.txt") || ($finalfile eq "base\.txt") )
         {
            #everytime there is a baseline revision the major revision of these 
            #two files will get incremented
            $cmd = "cvs commit -f -r $new_revision -m\"$new_label\" $finalfile";
            $response = `$cmd`;
         
            $cmd = "cvs rtag -r $new_revision $new_label $project/firmware/code/exe/$finalfile";

         
         }
         else
         {
            $cmd = "cvs commit -f -m\"$new_label\" $finalfile";
            $response = `$cmd`;
            
            
            #mark each file with the new label
            $cmd = "cvs rtag -r $exec_rev $new_label $project/firmware/code/exe/$finalfile";
         }
            
         $response = `$cmd`;
      
      }
      #the cvs response is nothing when there are no errors; if there is, 
      if($response ne "")
      {
         print "WARNING: Couldn't tag $finalfile: $response\n";
      }
   }


}

###############################################################################
## WriteBaseLabelToSCRs()
##
## Description: Write the label of this build to the SCRS that were used.
##              Then they will not used for future builds since base.txt
##              will contain any revisions specified in them.
##
## Input:      None
##
##
## 
###############################################################################
sub WriteBaseLabelToSCRs
{
      
   my ($database_name,$data_source,$username, $password, $command);
   $comment = "Files baselined in build " . $new_label;

   #  Open and prepare the connection to the database.
   #
   $database_name = 'scr.pacing.medtronic.com';
   $data_source = "dbi:Oracle:$database_name";
   $username = 'scr_text';
   $password = 'scr_text';

   #$ENV{'ORACLE_HOME'} = 's:\\orawin32\\bin';
   $ENV{'TWO_TASK'} = $database_name;
   $ENV{'DBI_DSN'} = $data_source;

   $DBI::dbh = DBI->connect($data_source, $username, $password);
   $DBI::dbh->{AutoCommit} = 0;
   
   foreach $id (keys %AllScrIdList)
   {
   
      #  already has a null entry, first delete null entry
      my( $query) = 'delete from TextStrings where ID = ' . $id;
      $DBI::sth = $DBI::dbh->do( $query);
      $DBI::dbh->commit || print ("$DBI::sth->errstr \n");

      my( $query) = 'insert into TextStrings (ID, String) ' .
                    ' values (' . $id . ', ' . $DBI::dbh->quote( $comment) . ')';
                    
      print( "$query\n");
   
      $DBI::sth = $DBI::dbh->do( $query);
      $DBI::dbh->commit || print ("$DBI::sth->errstr \n");
   }

  
   if (defined $DBI::dbh) {$DBI::dbh->rollback; $DBI::dbh->disconnect;}


}
